perm filename POINTY.DOC[PNT,HE]3 blob
sn#330124 filedate 1978-01-26 generic text, type C, neo UTF8
COMMENT ⊗ VALID 00014 PAGES
C REC PAGE DESCRIPTION
C00001 00001
C00002 00002 PURPOSE OF THIS FILE
C00003 00003 IMPLEMENTATION COMMENTS
C00007 00004 THE PARSER
C00019 00005 THE SCANNER
C00024 00006 SYMBOL TABLE ORGANIZATION
C00031 00007 INITIALIZATIONS
C00039 00008 FILE TABLE ORGANIZATION
C00044 00009 DISPLAY
C00049 00010 HOW "KILL" IS IMPLEMENTED
C00054 00011 DEFAULT PARTS
C00058 00012 HOW "CONSTRUCT" IS IMPLEMENTED
C00061 00013 HOW TO HANDLE THE ROTATION MATRIX
C00069 00014 THE EXPRESSION EVALUATOR
C00071 ENDMK
C⊗;
PURPOSE OF THIS FILE
This file is a legacy of Maria Gini and Pina Gini left behind
to explain in an informal way the control structure of the final POINTY
system, and meant to help anybody who plans to modify/use POINTY.
The file was originally put together by Maria (whose programmer name is MLG)
whom we should verbally thank each time we use this file instead of
having to chase through half a dozen files trying to figure where is
what.
Shahid 1-5-78. [MSM]
IMPLEMENTATION COMMENTS
Some comments about some implementation choices are in next pages.
The program is in the file POINTY.SAI[PNT,HE].
It requires some source files and load modules
conditional source file load module
flag
MAINPR.SAI[PNT,HE]
MACROS.SAI[PNT,HE]
RECORD.DEF[PNT,HE]
PARSER.HDR[PNT,HE] PARSER[PNT,HE]
OPERAT.HDR[PNT,HE] OPERAT[PNT,HE]
OUTPUT.HDR[PNT,HE] OUTPUT[PNT,HE]
ARMINT.SAI[PNT,MSM] TLKF5a[PNT,HE]
#KILL KILLER.HDR[PNT,HE] KILLER[PNT,HE]
#HELP HELP.HDR[PNT,HE] HELP[PNT,HE]
#ARROW ARROW[PNT,HE]
#DISPL DISPLY[PNT,HE]
#OUTPT INPOUT[PNT,HE]
#MOVE ( TLKF3a[PNT,HE]
( MOVARM[PNT,HE]
( BEJCZY[PNT,HE]
( ARMSOL[PNT,HE]
file purpose
HELP used by HELP.SAI to read the explanations about the syntax.
POINTY.SAI outermost file with specified compilation flags for a complete
version of POINTY.
MACROS.SAI useful macro definitions
RECORD.DEF external declarations for the record classes, the
symbol table and the pointers to the predefined variables
PARSER.SAI basic parsing procedures
OPERAT.SAI basic arithmetic operations
EXPR.SAI procedures for parsing and calling the arithmetic expressions
OUTPUT.SAI procedures to construct the string with the decoded
values of the different variables for printing
DISPLY.SAI display procedures
INPOUT.SAI for file output (read is in MAINPR)
KILLER.SAI procedures required by KILL instruction
HELP.SAI procedures for the complete error explanations and syntax
messages
ARROW.SAI contains procedure for drawing arrow on the display.
**********************
The previous working version of POINTY is POINTY.OLD[PNT,HE].
The dump file is OLD.DMP[PNT,HE]. This was the state of the world
as of end of December, 1977, and meant as backup in case there
are some bugs in the new version.
THE PARSER
One line is read on tty and is saved in $LINE. $LINE may contain more (or
less) than one instruction.
For error messages another variable is used, $NEXT. $NEXT at the
beginning is the same as $LINE. If $LINE contains more than one
instruction, each time an instruction has been parsed $NEXT is updated to
be the part of $LINE still to be parsed. If the instruction in $LINE is
not complete the system waits for a new line: in this case $NEXT is
updated by attaching the new line. So, no matter how the instructions have
been typed in they are saved in $NEXT for error messages.
$TAIL contains one instruction (if a break character has been found) or a
part of it. During the parsing process $TAIL is continuously updated to be
the part of the instruction not yet parsed. It'important to notice that if
no break character is found a <CR> is attached at the end of $TAIL. So the
scanner may know when the end of the instruction or of the line has been
reached, and operate subsequently.
Different break tables are used by the parser and the scanner.
The general table used in the main loop to separate one instruction from
the others is $SCNTAB. Its specifications are expressed in SAIL by
SETBREAK ($SCNTAB←GETBREAK,";?{",CR&LF&FF&TV,"INAK");
The break characters are ";", which is the normal termination of the
instruction, "?", which is used for help instruction and "{", which
indicates the beginning of a comment.
In that last case a special break table, $CMNTAB, is used to find the
matching "}"; its specifications are expressed in SAIL by the instruction
SETBREAK ($CMNTAB←GETBREAK,"}",NULL,"INA");
The parsing is done by the procedure PARSE.
The basic idea in PARSE is to check the first token of the instruction to
detect what kind of instruction it is. In fact the instructions begin
with a reserved word, unless they are assignmemt instructions (or the
reserved word is mispelled!!). To do the checking in a faster way the
first letter of the first word is checked and then a case table is used.
Then different procedures are called, to parse the different instructions.
THE SCANNER
The procedure GETTOKEN reads the next token in the string $TAIL and
returns it in TOKEN. The syntactic type of TOKEN is in #TOKEN.
It is ID_TYPE for identifiers,
INT_TYPE for integer numbers,
REAL_TYPE for real numbers,
OPERATOR_TYPE for punctuation marks, operators..
UNDECLARED_TYPE for undeclared variables,
RESERVED_TYPE for reserved words.
GETTOKEN uses different break tables
BTABLE←".,;[]()+-*/←↑↓→?α$"&LF&CR&TAB&FF&SP;
SETBREAK ($RETAB ←GETBREAK,BTABLE,NULL,"INR");
is used to read the token, retaining the break character
SETBREAK ($SKTAB ←GETBREAK,BTABLE,NULL,"INS");
is used to read the token after using $RETAB, just to skip the break
character left before in the string
SETBREAK ($SPCTAB←GETBREAK,TAB&SP,NULL, "XNR");
is used to skip the blanks before the token
SETBREAK ($ALFTAB←GETBREAK,NULL,NULL,"XRN");
is used to read one character from the string, usually to check if it's a
number or not
SETBREAK ($NUMTAB←GETBREAK,"@+-0123456789",NULL,"XNR"); ! as table 10;
is used while reading a number to detect the first non-number character
The idea of GETTOKEN is to skip the blanks, then to read the token
retaining the break character. The action is then dictate by the token and
the break. If the token is null there are two cases: can be a number (as
.12@3) or simply a punctuation mark(as . or ,) or the end of the string in
one instruction requiring more typing(instruction typed in different
lines). So these cases are identified taking a look at the break
character and deciding either to wait for more input (break=<cr> and flag
indicating the instruction is not terminated), or to return a punctuation
mark or again to continue reading a number. If the break character is a .
two cases are possible, because it can be part of a floating number or can
be only a punctuation mark.
If the token read at the beginning is not null two different cases are
possible: the break is a . or is a different character so the token is an
identifier.
In the first case (break .) the string is scanned to see if the token is
composed only by numbers (in that case is a floating number and other more
numbers can be present after .) or if the token is simply an identifier
followed by a .
If after this process the token results to be an identifier there is
another check to be done, because it can be an integer number. So the
token is scanned again, looking at the first character to see if it's a
number. In that case the other characters are checked to define the
correct type of the token.
Other break tables are used for different purposes.
* $DSTTAB is used by COPY/MERGE instructions to find if there is an
underscore in the name of the frames.
* $ERRTAB is used by the recover procedure to check if the new name typed
is an identifier. If a break character is found the recover will proceed
asking to type again a correct isdentifier. During the scanning spaces and
<cr>'s are eliminated.
* $BSKTAB is used for the display to compact the output, by erasing the
spaces.
SYMBOL TABLE ORGANIZATION
The symbol table is constituted by an array $YMTAB, whose elements are
rptr's to records of class SYMBOL.
* the number of positions in $YMTAB is #LMT+1, with #LMT=499.
The symbol table is then divided in parts, one for each type of variables.
* the number of the types is #NTYPE, with #NTYPE=#MAX-#MIN+1
where #MIN = 1 and #MAX = 5.
They corresponds to SCALAR, VECTOR, ROT, FRAME, TRANS.
* the number of positions for each type is #LTYPE. It's given by
#LTYPE= 100.
* the type SCALAR is in the first part of $YMTAB (#SC=1=#MIN), the
type VECTOR in the second (#VT=2), the type ROT in the third (#RT=3),
the type TRANS in the fourth (#TR=4) and the type FRAME in the fifth
(#FR=5).
* #SC, #VT, #RT, #FR and #TR are used to enter in the appropriate
part of $YMTAB.
The records of class SYMBOL have two fields
pname, is the pname of teh symbol (string),
object, is the rptr to the record of the appropriate class.
The integer array $ENTRY is used to keep trace of the number of elements
inserted in $YMTAB.
* the number of positions in $ENTRY is #NTYPE
* each position in $ENTRY corresponds to one type of variables
* each position contains the entry in $YMTAB of the first position
free for the corresponding type.
* the range of values for the i-element of $ENTRY (orresponding to all
the entries in $YMTAB for the class i) is given by
#LTYPE*(i-#MIN)≤ $ENTRY[i] ≤ (#LTYPE*i)-#MIN
The different types of variables are stored on different types of records.
* SCALAR
scalar:value contains the real value of the scalar
* VECTOR
vector:xc contains the real value of the component along x-axis
vector:yc contains the real value of the component along y-axis
vector:zc contains the real value of the component along z-axis
* ROT
rot:xf is a real array[1:5,1:4], as in FRAME
* FRAME
frame:pname contains the pname of the frame (string)
frame:dad contains the pointer to its dad in the tree
frame:son contains the pointer to its last son
frame:ebro contains the pointer to its elder brother
frame:ybro contains the pointer to its younger brother
frame:howlinked contains the kind of affixment(#RGDLK,#NRGLK,#INDLK)
frame:xf is a real arry containing
xf[1:3,1:3]=rotation matrix,
xf[1:3,4]=translation vector,
xf[4,1:3]=0,
xf[4,4]=1,
xf[5,1:3]=rotation angles (euler angles)
xf[5,4]>0 if angles are valid;
* TRANS
trans:xf is a real array [1:5,1:4], as in FRAME
SYMTAB[0:lmt] symbol scalar
______ _______________ _________
| | | | | | |
| -|---------→|pname|object-|--------→| value |
|____| |_____|_______| |_______|
| |
| -|-----→..
|____|
| |
| .. | vector
|____| _______________ __________
| | | | | | | | |
| -|---------→|pname|object-|--------→|xc|yc|zc|
|____| |_____|_______| |__|__|__|
| |
| -|-----→..
|____|
| |
| .. | rot
|____| _______________ ____________
| | | | | | |
| -|---------→|pname|object-|--------→|xf(array) |
|____| |_____|_______| |__________|
| |
| -|-----→...
|____|
| |
| .. | frame
|____| _______________ ______________________________________
| | | | | | | | | | | | |
| -|---------→|pname|object-|--------→|pname|xf|dad|son|ebro|ybro|howlinked|
|____| |_____|_______| |_____|__|_|_|_|_|_|__|_|__|_________|
| | ↓ ↓ ↓ ↓
| -|-----→.. frame
|____|
| |
| .. | trans
|____| _______________ ____________
| | | | | | |
| -|---------→|pname|object-|--------→|xf(array) |
|____| |_____|_______| |__________|
| |
| -|-----→...
|____|
| |
| .. |
|____|
There are many predeclared symbols. Their pointers and values are described
in the session about initialization.
Some other pointers are defined, to keep track of some important information,
or to define some variable internally used, as
rptr(frame)
F_ARM, used to remember what arm is holding the pointer,
F_FID used to store the rptr to the FIDUCIAL point (when defined)
rptr(trans)
ARRAY T_CSTR[1:3] used to store the frames defined by CONSTRUCT
without arguments
INITIALIZATIONS
PROCEDURES REQUIRING TO BE INITIALIZED
INIBRK
initializes the break tables
initializes the string $BLANK
sets the number of decimal characters for real numbers to 3.
INISYM
initializes the symbol table.
VARIABLES REQUIRING TO BE INITIALIZED
$ALFL←"DECLAR.AL"
default name for input/output file
$EPS = 0.001
is a value used for tests while using trigonometric functions
$READ←FALSE
used by readcode: true while reading
PREDECLARED POINTY VARIABLES
the first element is the pname (printname) of the variable
the second is the record_pointer to the record of class SYMBOL
the third is the record_pointer to the record of the particular
class (SCALAR,VECTOR,ROT,TRANS,FRAME). These names begin with the
initial of the class name and an underscore
the fourth is the value. Values for rot, frame and trans are expressed
by six elements (w,ph,th,x,y,z) corresponding to
(rot(zhat,th)*rot(yhat,ph)*rot(zhat,w),vector(x,y,z))
pname(string) rptr(symbol) rptr(scalar) value
BHAND HANDB S_BHAND
YHAND HANDY S_YHAND
rptr(vector)
XHAT XHAT V_XHAT (1,0,0)
YHAT YHAT V_YHAT (0,1,0)
ZHAT ZHAT V_ZHAT (0,0,1)
NILVECT NILVECT V_NILVECT (0,0,0)
rptr(rot)
NILROTN NILROTN R_NILROTN (0,0,0) euler angles
rptr(frame)
STATION WORLD F_WRLD (0,0,0,0,0,0)
BPARK BPARK F_BPARK (0,180,0,
43.53125,56.855,9.95875)
YPARK YPARK F_YPARK (0,180,0,40,14,9)
BARM BARM F_BARM
YARM YARM F_YARM (0,0,0,0,0,0)
BGRASP BGRASP F_BGRASP (-180,180,0,0,0,0)
affixed to BARM
POINTER POINTER F_POINTER (-.417,13.2,-5.173,
.0121,.119,3.75)
affixed to BARM
rptr(trans)
NILTRANS NILTRANS T_NILTRANS (0,0,0,0,0,0)
******* important notice
If other predefined variables are inserted the values in the array SAVE of
the procedure RESET in MAINPR.SAI have to be accordingly modified;
FILE TABLE ORGANIZATION
POINTY allows to write on different files, to save or to close them. The
names of the files used, and their status are mantained in a file table,
with an index to know the number of used files. That table is constituted
by two matrices. The information about one file can be found in those two
matrices on the same row.
* $NAMEFL is a string array [1:10], used for the file names
* $CHNFL is an integer array [1:10,0:1], containing in the first
column the status open(0)/close(1) of the file and in the second
column the number of the channel associated.
* $TOTFL is an integer, whose value is the total number of files
used.
$NAMEFL $CHNFL
______________ __________________________
|name of file| |open/close | channel # |
|(string) | | 0 1 | |
|____________| |___________|____________|
| | | | |
| | | | |
The last file used for output is the default name for output instruction.
We'll call this "current file".
* $ALFL, is a string containing the name of the current file
(initialized with DECLAR.AL). When the current file is closed the
name of a previously used open file is taken as default, or, if
there are'nt any DECLAR.AL is taken. The current file is indicated
on the display by a * before the file name.
* $ALCH is the channel number of the current file.
The output of TTY can be saved in a file. The name of this file is given
at the start of the session, and can be changed only by teh instruction
CLOSE_FILES. In the case the TTY output has to be recorded same variables
are used
* $OUT, is used as a flag to know if the output has to be saved
* $TTYFL is the name of the file
* $TTYCH is the channel number.
The input in TTY is converted in upper cases, so lower or upper cases can
be used in any combination. There are problems only while recovering,
because the characters typed into the line editor are not converted.
For READ instruction the global variables used are
* $READ, used as flag to know if teh instruction being executed is
a READ instruction. The flag is required to continue with the
reading in the case the file contains an error, since each error
causes a goto to the main loop
* $INPCH is the number of the channel associated with the input
file. It'used to remember the channel number after an error, and
it's also used by SAVE instruction, when the saved file is read
and copied.
DISPLAY
Some of the procedures for the display are in DISPLY.SAI[1,MLG].
The global variables used in that file are prefixed by ∂.
That file requires
source file load_module
DPYSUB.HDR[SUB,SYS] DPYSUB.SAI[SUB,SYS] DPYSUB[SUB,SYS]
The procedure INIDPY initializes the display, assigning the appropriate
values to the variables defining margin positions.
∂DLMAR ∂SCFR ∂DRMAR
↓ ↓ ↓
_________________________________
∂DTMAR→ | | |
| | |
| FRAME TREE | SC |
| TRANS |-------| ← ∂SCDF
∂TRFL→ |_______________________|_______|
| | | |
| FILE | ROT | VT |
|__________|__________|_________|
∂TPMAR→
↑ ↑
∂FLRT ∂RTVT
∂DBMAR →
The internal/external variables are
$ARROW position of the arrow
external integer
$BLANK string of blanks
external string
$BRCHR break character
external integer
$BSKTAB break table used to get ride of blanks
external integer
$DPYTAB breaktable used to split the string
external integer in different lines)
$EPS
external real
$NCHAR number of characters for frame space
internal integer (=∂wfr/∂chwid)
The external procedure used is
EULERO(REAL ARRAY XF;REFERENCE REAL W,PH,TH);
The global variables of DISPLY.SAI are
∂BUF buffer for the display
integer array [1:1000]
∂CHWID width of a character(=15 on DD,12 on III)
∂CHIGH height of a line (=20)
∂DBMAR bottom margin of the display (=-510 on DD,
-450 on III)
∂DLMAR left margin (=-625 on DD,-510 on III)
∂DRMAR right margin (=580 on DD, 510 on III)
∂DTMAR top margin (=450)
∂DWNLNS number of lines for lower part of display
(=12)
∂FLRT margin between files and rot's
(=-330 on DD,-215 on III)
∂RTVT margin between rot's and vectors
(=175 on DD, 147 on III)
∂SCDF margin between defaults and scalars(=-10)
∂SCFR margin between frames and scalars
(=400 ON DD,330 ON III)
∂SIZE size of the characters (=2)
∂TPMAR typing space top margin (=-318 on DD,
-270 on III)
∂TRFL trans's bottom margin (=-70)
∂UPLNS number of lines in upper part of display
(= 26)
∂WFR width of space for frame tree
(=1015 on DD,830 on III)
∂WRTVT width of space for vectors and rot's
(=495 on DD,352 on III)
∂WSC width of space for scalars (=170)
The procedure ARROW draws an arrow on the screen
. 80 . 20 .
c3y ..................3.....................
. |\ . 10
c12y 1 ________________2| \ .................
| . \ .
c4y | . \4 20
| . /.
|__________________ /................
c67y 7. 6| / . 10
c5y ..................|/....................
. 5 .
. . .
c17x c2356x c4x ;
It's possible to move by one line the array with the instruction ↑ , ↓ than can
be preceded by any integer number.
HOW "KILL" IS IMPLEMENTED
The kill instruction allows to delete the last instruction and its side
effects. Each time an instruction is parsed the kill is initialized by
INIKIL, which sets to null_record the record pointer to the record (or
list of records) used to save the information about the variables being
modified. That record pointer is KILL.
With that implementation would be very easy to save more than one
instruction, allowing so to delete more than the last one. It's enough to
save not only one record pointer (for the last instruction), but more
record pointers (in a circular list)and for each instruction delete the
oldest and insert the new one.
The records used are of class SAVED, where
saved:addr is the address (row number) in $YMTAB of the symbol
modified.
saved:type is the type of that symbol (#sc,#vt,#rt,#fr,#tr) or
is a special type #nw, for new symbols, or #nwfr, for
new frames. That choice ease the handling of the record.
saved:symbol is the record pointer to the record of class symbol.
If the symbol is a new defined one is null_record.
saved:object is the record pointer to the record of one type (#sc,
#vt,#rt,#fr,#tr) if the record is created in the same
instruction (so the type is #nw or #nwfr) or to the record
used to copy the value of the modified variable.
saved:dad is the record pointer to the dad of the frame (used only
when tree modifications are involved).
saved:link is the kind of affixment (same as before).
saved:next is the pointer to next record of the same type. All
the records used to save information about one POINTY
instruction are connected by this link, so it's possible
to follow that chain to restore the previous situation.
How to save the information
There are two main procedures to save the status of a variable
SAVNEW, saves the status of a variable being defined in the instruction
itself. In that case saved:addr is taken from $ENTRY, and saved:object
is the rptr to the symbol. So if the instruction is killed is enough
to delete the symbol from $YMTAB.
SAVOLD, saves the status of a variable previously defined which
is modified in the instruction. A variable is used to remember the
address in $YMTAB of teh last checked symbol, $ROW. Its value is
inserted in saved:addr. Then a new record is constructed
How to recover
To know what was the last instruction a variable $LAST is used. The different
types of instructions are
kil, for not killable instructions
decl, for declarations
del, for deletion
asg, for assignment
afx, for affix/unfix
cpy, for copy and merge
DEFAULT PARTS
POINTY provides many default parts in different instructions.
For the instructions of movement there is a part of the instruction that
can be left out. The system remembers the last movement instruction used
and supply the first part of it as default. That part is displayed on the
little box below the scalar box on the display.
Two global variables are used to store that default part (OLDCMD and OLDOBJ).
default part part of the instruction to be typed in
____________________________________________________________________________________
OLDCMD OLDOBJ
____________________________________________________________________________________
MOVE <frame_id> BY <vector>
TO <frame_id> {+<vector> {WRT <frame_id>}}
MOVEX {<frame_id>} BY <scalar>
MOVEY {<frame_id>} BY <scalar>
MOVEZ {<frame_id>} BY <scalar>
OPEN <hand> TO <scalar>
BY <scalar>
CLOSE <hand> TO <scalar>
BY <scalar>
DRIVE BJT(<number>) TO <scalar>
BY <scalar>
Other instructions allow a default part
instruction default
___________________________________________________________________________________
BARM← INPUT BARM (to update arm position)
CENTER BARM
CLOSE current file
<id>←INPUT POINTER
YARM← INPUT YARM (to update arm position)
OPEN TO|BY <scalar> BHAND
MOVEX BY <scalar> BARM
MOVEY BY <scalar> BARM
MOVEZ BY <scalar> BARM
READ DECLAR.AL
SAVE current file
WRITE current file FROM STATION
WRITE FROM <frame_id> current file
WRITE <file> FROM STATION
vector, rot.... *INCH, *INCHES, *DEG, *DEGREES (no dimension check)
HOW "CONSTRUCT" IS IMPLEMENTED
Three positions are required to define a reference system:
at the origin
on one main axis
on the plane between the previous axis and another main axis
So, given three vectors v1, v2 and v3, and knowing
v1 is at the origin of the frame
v2 is on the axis first_axis (f_axis)
v3 is on the plane through f_axis and second_axis (s_axis)
we want to determine the third_axis (t_axis).
We'll associate an integer number to each one of the main axes, to ease
the computations
xhat 1
yhat 2
zhat 3
Those numbers relates the name of the axis to the number of the column in
the rotation matrix corresponding to it.
* the vector v1 gives the position of the new frame
(column 4 of the rotation matrix);
* the vector |v2-v1| gives the f_axis
(columm f_axis of the rotation matrix);
To determine the other two main axes (column s_axis and t_axis of the
rotation matrix) there are two different cases:
a) b)
↑ f_axis ↑ f_axis
v2 v2
v3 | | v3
| |
v1-----------→ t_axis v1------------→ s_axis
/ /
s_axis t_axis
/ /
t_axis = ||v2-v1|*|v3-v1|| t_axis = ||v3-v1|*|v2-v1||
s_axis = t_axis * f_axis s_axis = f_axis * t_axis
To determine what is the appropriate case it's enough to check if s_axis
follows f_axis in the regular order (case a) or not (case b). This check,
using the numbers associated to the axes, is a simple test on
permutations.
HOW TO HANDLE THE ROTATION MATRIX
POINTY uses a rotation matrix [1:5,1:4],
the part [1:3,1,3] is the rotation matrix
the part [1:3,4] is the translation part
the part [4,1:4] is used only to ease the operations
the part [5,1:3] contains the euler angles corresponding to the rot
the element [5,4] is >0 when the euler angles are valid
!!!!!!! WARNING the rotation matrix [1:3,1:3] used by POINTY is the transpose
of the rotation matrix used by the interface and by AL.
* how to compute the rotation matrix
The procedure XYZROT used to compute the rotation matrix, for a rotation
of an angle W about the axis V (with components CX, CY, CZ about the main
axes ) constructs the matrix with the following formulas
MATRIX ROT(3,3)$
ROT(1,1) := - COS(W)*CX**2 + COS(W) + CX**2$
ROT(1,2) := - COS(W)*CX*CY + CX*CY - CZ*SIN(W)$
ROT(1,3) := - COS(W)*CX*CZ + CX*CZ + CY*SIN(W)$
ROT(2,1) := - COS(W)*CX*CY + CX*CY + CZ*SIN(W)$
ROT(2,2) := - COS(W)*CY**2 + COS(W) + CY**2$
ROT(2,3) := - COS(W)*CY*CZ - CX*SIN(W) + CY*CZ$
ROT(3,1) := - COS(W)*CX*CZ + CX*CZ - CY*SIN(W)$
ROT(3,2) := - COS(W)*CY*CZ + CX*SIN(W) + CY*CZ$
ROT(3,3) := - COS(W)*CZ**2 + COS(W) + CZ**2$
(the previous rows are in a form readable by REDUCE 2)
The procedure SETROT is used to compute directly the rotation matrix
corresponding to ROT(ZHAT,TH)*ROT(YHAT,PH)*ROT(ZHAT,W) given the Euler
angles W, PH and TH (see next section).
* how to decode the rotation matrix
Usually the matrix is decoded computing the Euler angles.
To show how the decode process is done we'll show the symbolic product of
three rotations
ROT(ZHAT,TH)*ROT(YHAT,PH)*ROT(ZHAT,W)
(the interpretation of the product between rotations is the same as in AL)
given the three rotation matrices
MATRIX MATZTH(3,3); COMMENT ROT(ZHAT,TH);
MATZTH(1,1) := COS(TH);
MATZTH(1,2) := - SIN(TH);
MATZTH(1,3) := 0;
MATZTH(2,1) := SIN(TH);
MATZTH(2,2) := COS(TH);
MATZTH(2,3) := 0;
MATZTH(3,1) := 0;
MATZTH(3,2) := 0;
MATZTH(3,3) := 1;
MATRIX MATZPH(3,3); COMMENT ROT(YHAT,PH);
MATYPH(1,1) := COS(PH);
MATYPH(1,2) := 0;
MATYPH(1,3) := SIN(PH);
MATYPH(2,1) := 0;
MATYPH(2,2) := 1;
MATYPH(2,3) := 0;
MATYPH(3,1) := - SIN(PH);
MATYPH(3,2) := 0;
MATYPH(3,3) := COS(PH);
MATRIX MATZW(3,3); COMMENT ROT(ZHAT,W);
MATZW(1,1) := COS(W);
MATZW(1,2) := - SIN(W);
MATZW(1,3) := 0;
MATZW(2,1) := SIN(W);
MATZW(2,2) := COS(W);
MATZW(2,3) := 0;
MATZW(3,1) := 0;
MATZW(3,2) := 0;
MATZW(3,3) := 1;
we'll obtain the product
MATRIX EULER(3,3); COMMENT ROT(ZHAT,TH)*ROT(YHAT,PH)*ROT(ZHAT,W);
EULER(1,1) := COS(W)*COS(TH)*COS(PH) - SIN(W)*SIN(TH);
EULER(1,2) := - (COS(W)*SIN(TH) + SIN(W)*COS(TH)*COS(PH));
EULER(1,3) := COS(TH)*SIN(PH);
EULER(2,1) := COS(W)*SIN(TH)*COS(PH) + SIN(W)*COS(TH);
EULER(2,2) := COS(W)*COS(TH) - SIN(W)*SIN(TH)*COS(PH);
EULER(2,3) := SIN(TH)*SIN(PH);
EULER(3,1) := - COS(W)*SIN(PH);
EULER(3,2) := SIN(W)*SIN(PH);
EULER(3,3) := COS(PH);
The procedure EULERO extract from that matrix the angles W, PH, TH.
Sometimes we need to decode the rotation matrix in a different way. For
example to construct a matrix corresponding to the arm position, but with
the ZHAT pointing up vertically, we need to decode the matrix as a product
of rotations about the three main axes, and then to use only the component
of the rotation about ZHAT.
To show how the decode process is done we'll show the symbolic product of
three rotations
ROT(ZHAT,C)*ROT(YHAT,B)*ROT(ZHAT,A)
(the interpretation of the product between rotations is the same as in AL)
MATRIX MATX(3,3); COMMENT ROT(XHAT,A);
MATX(1,1) := 1;
MATX(1,2) := 0;
MATX(1,3) := 0;
MATX(2,1) := 0;
MATX(2,2) := COS(A);
MATX(2,3) := - SIN(A);
MATX(3,1) := 0;
MATX(3,2) := SIN(A);
MATX(3,3) := COS(A);
MATRIX MATY(3,3); COMMENT ROT(YHAT,B);
MATY(1,1) := COS(B);
MATY(1,2) := 0;
MATY(1,3) := SIN(B);
MATY(2,1) := 0;
MATY(2,2) := 1;
MATY(2,3) := 0;
MATY(3,1) := - SIN(B);
MATY(3,2) := 0;
MATY(3,3) := COS(B);
MATRIX MATZ(3,3); COMMENT ROT(ZHAT,C);
MATZ(1,1) := COS(C);
MATZ(1,2) := - SIN(C);
MATZ(1,3) := 0;
MATZ(2,1) := SIN(C);
MATZ(2,2) := COS(C);
MATZ(2,3) := 0;
MATZ(3,1) := 0;
MATZ(3,2) := 0;
MATZ(3,3) := 1;
we'll obtain the product
MATRIX MATPROD(3,3); COMMENT ROT(ZHAT,C)*ROT(YHAT,B)*ROT(XHAT,A);
MATPROD(1,1) := COS(C)*COS(B);
MATPROD(1,2) := SIN(A)*COS(C)*SIN(B) - SIN(C)*COS(A);
MATPROD(1,3) := SIN(A)*SIN(C) + COS(C)*SIN(B)*COS(A);
MATPROD(2,1) := SIN(C)*COS(B);
MATPROD(2,2) := SIN(A)*SIN(C)*SIN(B) + COS(C)*COS(A);
MATPROD(2,3) := - SIN(A)*COS(C) + SIN(C)*SIN(B)*COS(A);
MATPROD(3,1) := - SIN(B);
MATPROD(3,2) := SIN(A)*COS(B);
MATPROD(3,3) := COS(B)*COS(A);
The procedure DECODE decodes the matrix computing the three angles A, B
and C. When the angle B is 90 degrees it's impossible to compute A and C,
but it's only possible to compute their difference. The procedure DECODE
forces A to 0 and returns in C the value of (C-A).
THE EXPRESSION EVALUATOR
The expression evaluator is a top down single stage evaluator.
It tries to perform computations as soon as something is in a stage
that can be evaluated. No explicit tree of the parsed expression
is built up - an implicit tree is formed by means of the three recursive
routines that evaluate expressions, terms and factors.
The syntax of the three are as follows:
expressions E: <+ T | - T | T > {+T|-T}
term T: F { * F | / F }
factor F: ( E , E, E,... ) or | E |
or func(E,...)
or E WRT E
or E REL E
The expression evaluator (invoked by GTEXPR)
returns a record pointer to a record
of class TREE which have fields DATA and DTYPE.
DATA is a record pointer field of class SCALAR,VECTOR,TRANS,ROT or FRAME
DTYPE is an integer which indicates which record class DATA corresponds to.
The expression evaluator calls the following procedures:
GTOKEN which returns the next symbol and its type
OPSCAL,OPVET,etc which perform the actual arithmetic
ERROR which is defined by POINTY and is the routine for
handling error messages. Since ERROR does not return
control to the expression evaluator, continuation
of computation is started afresh with the corrected
expression.